package middleware

import (
	"context"
	"energy-amiot-platform/core"
	"energy-amiot-platform/repository"
	"energy-amiot-platform/utils/hash"
	"energy-amiot-platform/utils/respout"
	"errors"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"
	client "github.com/ory/hydra-client-go"
	"github.com/spf13/viper"
	"github.com/thoas/go-funk"
	"gorm.io/gorm"
)

var (
	moduleCode = 70
)

func ResolveProduct(ctx *gin.Context) {
	var handleCode = 3
	pid, err := strconv.ParseUint(ctx.Param("id"), 10, 64)
	if err != nil {
		respout.SendError(ctx, core.APP.Code, moduleCode, handleCode, 1, respout.StrFail, err, nil)
		ctx.Abort()

		return
	}

	background := context.Background()
	db := core.DATABASE.Cur().WithContext(background)
	tx, product := repository.PRODUCT.FindById(db, pid)
	switch {
	case tx.Error == gorm.ErrRecordNotFound:
		respout.SendError(ctx, core.APP.Code, moduleCode, handleCode, 1, respout.StrFail, tx.Error, nil)
		ctx.Abort()

		return
	case tx.Error != nil:
		respout.SendError(ctx, core.APP.Code, moduleCode, handleCode, 1, respout.StrFail, tx.Error, nil)
		ctx.Abort()

		return
	}

	ctx.Set("product", product)
}

func AuthMasterKey(ctx *gin.Context) {
	var handleCode = 1
	var masterKey = ctx.GetHeader("Master-Key")
	if masterKey == "" {
		respout.SendError(ctx, core.APP.Code, moduleCode, handleCode, 1, respout.StrFail, errors.New("需提供Master-Key"), nil)
		ctx.Abort()

		return
	}
	background := context.Background()
	db := core.DATABASE.Cur().WithContext(background)
	tx, product := repository.PRODUCT.FindByMasterKey(db, masterKey)
	switch {
	case tx.Error == gorm.ErrRecordNotFound:
		respout.SendError(ctx, core.APP.Code, moduleCode, handleCode, 1, respout.StrFail, errors.New("请提供正确的Master-Key"), nil)
		ctx.Abort()

		return
	case tx.Error != nil:
		respout.SendError(ctx, core.APP.Code, moduleCode, handleCode, 3, respout.StrFail, tx.Error, nil)
		ctx.Abort()

		return
	}

	ctx.Set("product", product)
	ctx.Next()
}

func AuthApp(ctx *gin.Context) {
	var handleCode = 2
	var key = ctx.GetHeader("App-Key")
	var secret = ctx.GetHeader("App-Secret")
	var appHash = ctx.GetHeader("App-Hash")
	if appHash == "" {
		if key == "" {
			respout.SendError(ctx, core.APP.Code, moduleCode, handleCode, 1, respout.StrFail, errors.New("需提供App-Key"), nil)
			ctx.Abort()

			return
		}
		if secret == "" {
			respout.SendError(ctx, core.APP.Code, moduleCode, handleCode, 1, respout.StrFail, errors.New("需提供App-Secret"), nil)
			ctx.Abort()

			return
		}

		appHash = hash.GenerateAppSecretHash(key, secret, "hkfg+")
	}

	result, err := core.REDIS.Exists(context.Background(), "mqtt_app:"+appHash).Result()
	switch {
	case err != nil:
		respout.SendError(ctx, core.APP.Code, moduleCode, handleCode, 1, respout.StrFail, err, nil)
		ctx.Abort()

		return
	case result == 0:
		respout.SendError(ctx, core.APP.Code, moduleCode, handleCode, 1, respout.StrFail, errors.New("无app权限"), nil)
		ctx.Abort()

		return
	}

	ctx.Next()
}

func AuthToken() gin.HandlerFunc {
	config := client.NewConfiguration()
	config.AddDefaultHeader("x_secret", "jb3VZKCTyjt11lk5ds8ElJ")
	oauthEnable := viper.GetBool("oauth2.enable")
	ignorePaths := viper.GetStringSlice("oauth2.ignore")
	oauthAdminUrl := viper.GetString("oauth2.admin-url")
	//oauthClientId := viper.GetString("oauth2.client-id")
	config.Servers = []client.ServerConfiguration{
		{
			URL: oauthAdminUrl,
		},
	}
	apiClient := client.NewAPIClient(config)

	return func(c *gin.Context) {
		if !oauthEnable {
			c.Next()
			return
		}

		if strings.HasPrefix(c.Request.URL.Path, "/swagger") {
			c.Next()
			return
		}

		token := c.Request.Header.Get("Authorization")
		s := c.Request.URL.Path + "|" + c.Request.Method
		of := funk.IndexOf(ignorePaths, strings.ToLower(s))
		if -1 != of {
			c.Next()

			return
		}

		if len(token) == 0 {
			c.AbortWithStatusJSON(401, gin.H{
				"msg": "need token",
			})

			return
		} else {
			token = strings.Replace(token, "Bearer ", "", 1)
			execute, _, err := apiClient.AdminApi.IntrospectOAuth2Token(context.Background()).Token(token).Execute()
			if err != nil {
				c.AbortWithError(400, err)
				return
			}

			if !execute.Active {
				c.AbortWithStatus(401)
				return
			}

			//if *execute.ClientId != oauthClientId {
			//	c.AbortWithStatus(401)
			//	return
			//}

			isAuth := strings.Contains(*execute.Scope, "amiot.platform.super")
			if !isAuth {
				c.AbortWithStatus(401)
				return
			}
		}

		c.Next()
	}
}
